﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace FluentUnity.Conventions
{
    public class ImplementationSelectionConvention
    {
        public ImplementationSelectionConvention()
        {
            SelectionStrategies = new List<Func<Type, Type>>();
        }

        public void Select(Func<Type, Type> strategy)
        {
            SelectionStrategies.Add(strategy);
        }

        private List<Func<Type, Type>> SelectionStrategies { get; set; }

        internal Type Select(Type @interface)
        {
            Ensure.IsNotNull(() => @interface);

            Type implementation = null;
            if (SelectionStrategies.Count > 0)
            {
                implementation = SelectionStrategies
                    .Select(s => s(@interface))
                    .Where(i => i != null && @interface.IsAssignableFrom(i))
                    .FirstOrDefault();
            }
            else
            {
                var attribute = @interface.GetAttribute<ImplementationAttribute>();
                implementation = attribute != null
                                     ? attribute.Type
                                     : @interface.Assembly.GetType(@interface.Namespace + "." + @interface.Name.TrimStart('I'), false);

            }
            if (implementation == null)
                return null;

            if (!@interface.IsAssignableFrom(implementation))
                throw new InvalidOperationException(String.Format("Located implementation of type {0} for interface {1} which doesn't implement the interface", implementation,
                                                                  @interface));

            return implementation;
        }
    }
}